import java.util.*;

public class Ball extends AttachableElement  {
   private static int id=0;  // Ball identification number
   private double crossSection;
   private float mass;
   private Vector2D pos_t;     // current position at time t
   private Vector2D pos_tPlusDelta;  // next position in delta time in future
   private Vector2D speed_t;   // speed at time t
   private Vector2D speed_tPlusDelta;   // speed in delta time in future
   private Vector2D a_t;    // acceleration at time t
   private Vector2D a_tMinusDelta;  // acceleration delta time ago;
   private Vector<Spring> springs;  // Vector can grow, arrays cannot.
   private Vector<Rubber> rubbers;
   
   private Ball(){
     // nobody can create a block without state
     super(id++);
     mass=0;
   }
   public Ball(float mass, Vector2D position, Vector2D speed){
      super(id++);
      this.mass = mass;
      pos_t = position;
      speed_t = speed;
      a_t = new Vector2D();
      a_tMinusDelta = new Vector2D();
      springs = new Vector<Spring>();
      rubbers = new Vector<Rubber>();
      crossSection = 1.0f;
   }
   /**
    * modifica el valor de masa de la bola actual.
    * @param masa el nuevo valor de masa a agregar
    */
   public void set_mass(float masa){
       mass=masa;
   }
   /**
    * obtiene el valor de masa de la bola actual.
    * @return mass valor flotante de la masa de la bola.
    */
   public Float get_mass(){
   return mass;
   }

   public void attachSpring (Spring spring) {
     springs.add(spring);
   }
   public void detachSpring (Spring spring) {
     springs.remove(spring);
   }

    public void attachRubber(Rubber rubber) {
    rubbers.add(rubber);
    }

    public void detachRubber(Rubber rubber) {
    rubbers.remove(rubber);
    }

   public Vector2D getPosition() {
      return pos_t;
   }
   public double getX() {
      return pos_t.getX();
   }
   public double getY() {
      return pos_t.getY();
   }
   public void setPosition(double x, double y){
      pos_t.set(x,y);
   }
   public void setCrossSection(double cs) {
      crossSection = cs;
   }
   private Vector2D getNetForce(Vector2D g, float viscosity ) {
     Vector2D force = new Vector2D();
     for (Spring s: springs)
        force = force.plus(s.getForce(this));
     for (Rubber s: rubbers)
        force = force.plus(s.getForce(this));
     force = force.plus(g.times(-mass));
     force = force.minus(speed_t.times(viscosity*crossSection)); 
     return(force);    
   }
   public void setInitialState(Vector2D g, float viscosity) {
      a_tMinusDelta = getNetForce(g, viscosity).times(1/mass);
   }
   public void computeNextState(double delta_t, Vector2D g, float viscosity) {
     a_t = getNetForce(g, viscosity).times(1/mass);
     speed_tPlusDelta = speed_t.plus(a_t.times(3).minus(a_tMinusDelta).times(delta_t/2));
     pos_tPlusDelta = pos_t.plus(speed_t.times(delta_t)).plus(a_t.times(4).minus(a_tMinusDelta).times(delta_t*delta_t/6));
   }
   public void updateState(){
     pos_t = pos_tPlusDelta;
     speed_t = speed_tPlusDelta;
     a_tMinusDelta = a_t;
   }
   public String getDescription() {
      return "Ball_" + getId()+":"+Vector2D.getDescription();
   }
   public String getState() {
      return getPosition()+"";
   }

 /**
  * Función que re-inicializa las variables speed_t, a_t, speed_tPlusDelta
  * y a_tMinusDelta, para olvidar el estado del objeto y reiniciar la simulación.
  */
 public void reset_stat(){
      speed_t.set(0, 0);
      a_t.set(0, 0);
      if(speed_tPlusDelta != null) speed_tPlusDelta.set(0, 0);
      if(a_tMinusDelta != null) a_tMinusDelta.set(0, 0);
   }

}